#include<stdio.h>
#include<stdlib.h>
#include<string>
#include<iostream>
#include<fstream>
#include<GL/glew.h>
#include <GL/glut.h>
#include <GL/gl.h>
//#include <GL/glu.h>
#include <Cg/cg.h>
#include <Cg/cgGL.h>
#include <gputimer.h>

using namespace std;

string vpname = "invert.vert";
string fpname = "invert.frag";
GLuint texture;

int width = 512;
int height = 512;

GLfloat* image;
GLfloat* image2;

CGcontext cgContext;
CGprogram cgProgram;
CGprofile cgFragmentProfile;
CGparameter textureHandle = NULL;

void glPrintError() {
  GLenum glErr = glGetError();
    while (glErr != GL_NO_ERROR) {
    cout << "Err: " << gluErrorString(glErr) << endl;
    glErr = glGetError();
  }
}


void init()
{
//  cerr << "OpenGL Version: > " << glGetString(GL_VERSION) << " <" << endl;
	
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClearDepth(1.0);
  glDepthFunc(GL_LESS);
  glEnable(GL_DEPTH_TEST);
  glShadeModel(GL_SMOOTH);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glOrtho(-1.0f,1.0f,-1.0f,1.0f,0.0f,2.0f);
  glMatrixMode(GL_MODELVIEW);

  GLfloat depth = 1;
  image = (GLfloat*)malloc(height*width*4*sizeof(GLfloat));
  for (int i=0; i<height; i++) {

    for (int j=0; j<width; j++) {
      GLfloat color = 0;
      if (i < j) { color = depth; }
      int index = 4*((height*i)+j);
      image[index] = color;
      image[index+1] = color;
      image[index+2] = color;
      image[index+3] = depth;
//      if ((index%10000)==0) { cout << index << ": " << color << endl; }
    }
  }
  
  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB,texture);
  glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_S,GL_CLAMP);
  glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_T,GL_CLAMP);
  glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
  glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
  glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,0,4,width,height,0,GL_RGBA,GL_FLOAT,image);
  glEnable(GL_TEXTURE_RECTANGLE_ARB);

  cgContext = cgCreateContext();
  if (cgContext == 0) {
    cerr << "Cg Context creation failure" << endl;
    exit(-1);
  }
  cgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
  if (cgFragmentProfile == CG_PROFILE_UNKNOWN) {
    cerr << "Invalid Fragment Program profile" << endl;
    exit(-1);
  }
  cgGLSetOptimalOptions(cgFragmentProfile);
  cgProgram = cgCreateProgramFromFile(cgContext, CG_SOURCE, "invert.frag", cgFragmentProfile, "fp", 0);
  if (cgProgram == 0) {
    CGerror err = cgGetError();
    cerr << cgGetErrorString(err) << endl;
    exit(-1);
  }
  cgGLLoadProgram(cgProgram);
  textureHandle = cgGetNamedParameter(cgProgram, "data");

}

void ReSizeGLScene(int w, int h)
{
  if (h==0)
    h=1;
  width = w;
  height = h;
  
  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glOrtho(-1.0f,1.0f,-1.0f,1.0f,0.0f,2.0f);
  glMatrixMode(GL_MODELVIEW);
}

void DrawGLScene()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  glTranslatef(0.0f,0.0f,-1.0f);
  glBindTexture(GL_TEXTURE_RECTANGLE_ARB,texture);
  cgGLBindProgram(cgProgram);
  cgGLSetTextureParameter(textureHandle, texture);
  cgGLEnableTextureParameter(textureHandle);
  cgGLEnableProfile(cgFragmentProfile);
  float v_tex = height;
  float h_tex = width;
  glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);  
    glVertex2f(-1.0f, -1.0f);
    glTexCoord2f(0.0f, v_tex);
    glVertex2f(-1.0f, 1.0f);
    glTexCoord2f(h_tex, v_tex);
    glVertex2f(1.0f, 1.0f);
    glTexCoord2f(h_tex, 0.0f);
    glVertex2f(1.0f,-1.0f);
  glEnd();
  image2 = (GLfloat*)malloc(height*width*4*sizeof(GLfloat));
  glReadPixels(0,0,height,width,GL_RGBA,GL_FLOAT,image2);

  bool success = true;
  for (int i=0; i<height; i++) {
    for (int j=0; j<width; j++) {
      int index = 4*((height*i)+j);
      if (image[index] != (1-image2[index])) { cout << i << "," << j << ": " << image[index] << "/= 1 - " << image2[index] << endl; success = false;}
      if (image[index+1] != (1-image2[index+1])) { cout <<i<< "," << j << ": " << image[index+1] << "/= 1 - " << image2[index+1] << endl; success = false;}
      if (image[index+2] != (1-image2[index+2])) { cout <<i<< "," << j << ": " << image[index+2] << "/= 1 - " << image2[index+2] << endl; success = false;}
      if (image[index+3] != (image2[index+3])) { cout <<i<< "," << j << ": " << image[index+3] << "/= " << image2[index+3] << endl; success = false;}     
    }
  } 
  if (success) { 
    cout << "Success!" << endl;
  } else {
    cout << "Failure!" << endl;
  } 
  exit(0);
  cgGLDisableProfile(cgFragmentProfile);
  glutSwapBuffers();
}

int main(int argc, char **argv) 
{  
  glutInit(&argc, argv);  
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);  
  glutInitWindowSize(width,height);  
  glutInitWindowPosition(0, 0);  
  glutCreateWindow("Invert Demo"); 
  glewInit(); 
  glutDisplayFunc(&DrawGLScene);  
  glutIdleFunc(&DrawGLScene);
  glutReshapeFunc(&ReSizeGLScene);
  init();
  glutMainLoop();
  cgDestroyContext(cgContext);
  return 1;
}
